package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"iris-shop/common"
	"iris-shop/common/db/rabbitmq"
	"iris-shop/common/middleware"
	"iris-shop/common/models"
	"iris-shop/common/tools"
	"iris-shop/common/tools/encrypt"
	"net/http"
	"net/url"
	"strconv"
	"sync"
	"time"
)

var hostArr = []string{"127.0.0.1", "127.0.0.1"}

//数量控制接口服务器ip
var (
	GetOneIp = "127.0.0.1"
	GetOnePort = "8084"
)

var (
	localHost        = ""
	port             = "8085"
	rabbitMqValidate *rabbitmq.RabbitMQ
)

var hashConsistent *common.Consistent

//防止for请求，服务器设置间隔时间
var interval = 20

type BlackList struct {
	listArray  map[int]bool
	sync.RWMutex
}

var blackList = &BlackList{listArray: make(map[int]bool)}

func (b *BlackList) GetBlackListById(uid int) bool {
	b.RLock()
	defer b.RUnlock()
	return b.listArray[uid]
}

func (b *BlackList) SetBlackListById(uid int) bool {
	b.Lock()
	defer b.Unlock()
	b.listArray[uid] = true
	return true
}

//存控制信息
type AccessControl struct {
	sourceArray map[int]time.Time
	*sync.RWMutex
}

//创建全局变量
var accessControl = &AccessControl{sourceArray: make(map[int]time.Time)}

func (m *AccessControl) GetNewRecord(uid int) time.Time {
	m.RWMutex.RLock()
	defer m.RWMutex.Unlock()
	data := m.sourceArray[uid]
	return data
}

func (m *AccessControl) SetNewRecord(uid int) {
	m.RWMutex.Lock()
	defer m.RWMutex.Unlock()
	m.sourceArray[uid] = time.Now()
}

func (m *AccessControl) GetDistributedRight(req *http.Request) bool {
	uid, err := req.Cookie("uid")
	if err != nil {
		return false
	}

	hostRequest, err := hashConsistent.Get(uid.Value)
	if err != nil {
		return false
	}

	//判断是否是本机
	if hostRequest == localHost {
		return m.GetDataFromMap(uid.Value)
	} else {
		return GetDataFromOtherMap(hostRequest, req)
	}
}

// 获取本机map，并处理业务逻辑
func (m *AccessControl) GetDataFromMap(uid string) bool {
	uidInt, err := strconv.Atoi(uid)
	if err != nil {
		return false
	}

	if blackList.GetBlackListById(uidInt) {
		return false
	}

	data := m.GetNewRecord(uidInt)
	//这里处理业务逻辑
	if !data.IsZero() {
		if data.Add(time.Duration(interval)*time.Second).After(time.Now()) {
			return false
		}
 	}
 	m.SetNewRecord(uidInt)
	return true
}

//获取其他节点处理结果
func GetDataFromOtherMap(host string, r *http.Request) bool {
	hostUrl := "http://"+host+":"+port+"/checkRight"
	response, body, err := GetCurl(hostUrl, r)
	if err != nil {
		return false
	}
	if response.StatusCode != 200 {
		return false
	}
	if string(body) == "true" {
		return true
	}
	return false
}

func GetCurl(hostUrl string, request *http.Request) (response *http.Response, body []byte, err error) {
	uidPre, err := request.Cookie("uid")
	if err != nil {
		return
	}

	uidSign, err := request.Cookie("sign")
	if err != nil {
		return
	}

	client := &http.Client{}
	req, nil := http.NewRequest(http.MethodGet, hostUrl, nil)
	if err != nil {
		return
	}
	cookieUid := &http.Cookie{Name: "uid", Value: uidPre.Value,Path: "/"}
	cookieSign :=&http.Cookie{Name:"sign",Value:uidSign.Value,Path:"/"}

	req.AddCookie(cookieUid)
	req.AddCookie(cookieSign)
	response, err = client.Do(req)
	defer response.Body.Close()
	if err != nil {
		return
	}

	body, err = ioutil.ReadAll(response.Body)
	return
}

func Auth(w http.ResponseWriter, r *http.Request) error {
	err := CheckUserInfo(r)
	if err != nil {
		return err
	}
	return nil
}

func Check(w http.ResponseWriter, r *http.Request) {
	println("run....")
	queryForm, err := url.ParseQuery(r.URL.RawQuery)
	if err != nil || len(queryForm["productID"])< 0 {
		w.Write([]byte("false"))
		return
	}
	productString :=queryForm["productID"][0]
	fmt.Println(productString)
	//获取用户cookie
	userCookie,err:=r.Cookie("uid")
	if err !=nil {
		w.Write([]byte("false"))
		return
	}
	//1.分布式权限验证
	right:=accessControl.GetDistributedRight(r)
	if right == false{
		w.Write([]byte("false"))
		return
	}

	//数量控制，防止秒杀超卖
	hostUrl := "http://"+GetOneIp+":"+GetOnePort+"/getone"
	responseValidate, responseBody ,err := GetCurl(hostUrl, r)
	if err != nil {
		w.Write([]byte("false"))
		return
	}
	if responseValidate.StatusCode == http.StatusOK {
		if string(responseBody) == "true" {
			productId, err := strconv.ParseInt(productString,10,64)
			if err != nil {
				//记录日志
				w.Write([]byte("false"))
				return
			}
			userId, err := strconv.ParseInt(userCookie.Value, 10, 64)
			if err != nil {
				w.Write([]byte("false"))
				return
			}

			//向队列里发送消息
			message := models.NewMessage(userId,productId)
			byteMessage, err := json.Marshal(message)
			if err != nil {
				w.Write([]byte("false"))
				return
			}
			err = rabbitMqValidate.PublishSimple(string(byteMessage))
			if err != nil {
				w.Write([]byte("false"))
				return
			}

			w.Write([]byte("true"))
			return
		}
	}

	w.Write([]byte("false"))
	return
}

func CheckRight(w http.ResponseWriter, r *http.Request) {
	right := accessControl.GetDistributedRight(r)
	if !right {
		w.Write([]byte("false"))
		return
	}
	w.Write([]byte("true"))
	return
}

func CheckUserInfo(r *http.Request) error {
	uidCookie, err := r.Cookie("uid")
	if err != nil {
		return errors.New("用户信息获取失败")
	}
	signCookie, err := r.Cookie("sign")
	if err != nil {
		return errors.New("用户信息获取失败")
	}
	signByte, err := encrypt.DePwdCode(signCookie.Value)
	if err != nil {
		return errors.New("解密失败,信息已被串改")
	}
	println(string(signByte))
	if checkInfo(uidCookie.Value, string(signByte)) {
		return nil
	}
	return errors.New("解密失败,身份验证不通过")
}

func checkInfo(checkStr string, signStr string) bool {
	if checkStr == signStr {
		return true
	}
	return false
}

func main() {
	//负载均衡
	hashConsistent = common.NewConsistent()
	for _, i := range hostArr {
		hashConsistent.Add(i)
	}

	localIp, err := tools.GetIp()
	if err != nil {
		fmt.Println(err.Error())
	}
	localHost = localIp
	fmt.Println("ip:" + localIp)

	rabbitMqValidate = rabbitmq.NewRabbitMQSimple("iris-shop")
	defer rabbitMqValidate.Destory()

	http.Handle("/html/",http.StripPrefix("/html/", http.FileServer(http.Dir("./app/fronted/web/htmlProductShow"))))
	http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("./app/fronted/web/public"))))

	filter := middleware.NewFilter()
	filter.RegisterFilterUri("/check", Auth)
	filter.RegisterFilterUri("/checkRight", Auth)
	http.HandleFunc("/check", filter.Handle(Check))
	http.HandleFunc("/checkRight", filter.Handle(CheckRight))
	err = http.ListenAndServe(":8085", nil)
	if err != nil {
		panic(err.Error())
	}
}
